package huu.phong.youtube;

import huu.phong.youtube.ConstantValue.SearchType;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class YouTubeAPI {
	public static final int MAX_RESULT = 10;
	private static YouTubeAPI youtube;
	private DecimalFormat formatter = new DecimalFormat("#,###,###");
	private Pattern p = Pattern.compile("\\b([\\p{javaLowerCase}])", Pattern.UNICODE_CASE);  
	String fields = "entry(yt:hd,author(name,yt:userId),media:group(media:title(),media:description(),yt:duration,yt:videoid),yt:statistics(@viewCount))";
	public String defaultFilter = "&alt=json&prettyprint=true&max-results=";
	private static final Pattern pattern = Pattern.compile("(\\[\\d+-\\d+\\])");
	private static final Pattern numberPattern = Pattern.compile("(\\d+)");
	
	public static YouTubeAPI getInstance(){
		if (youtube == null) youtube = new YouTubeAPI();
		return youtube;
	}
	
	public List<Video> search(String value, int page, String filter) throws UnsupportedEncodingException, IOException{
		Matcher matcher = pattern.matcher(value);
		List<String> numbers = new ArrayList<String>();
		boolean partVideo = false;
		while (matcher.find()){
			numbers.add(matcher.group(1));
			partVideo = true;
		}
		if (partVideo) return searchPartVideo(value, page, filter, numbers);
		return getSong("http://gdata.youtube.com/feeds/api/videos?v=2&q=" + URLEncoder.encode(value, "UTF-8"), page, MAX_RESULT, filter);
	}
	
	private int[] getRange(String value){
		int[] ret = new int[2];
		Matcher matcher = numberPattern.matcher(value);
		for (int i = 0; matcher.find(); i++){
			ret[i] = Integer.parseInt(matcher.group(1));
		}
		return ret;
	}
	
	public List<Video> searchPartVideo(String value, int page, String filter, List<String> numbers) throws UnsupportedEncodingException, IOException{
		List<Video> songs = new ArrayList<Video>();
		List<Video> temp = new ArrayList<Video>();
		List<String> titles = new ArrayList<String>();
		titles.add(value);
		for (int i = 0; i < numbers.size(); i++){
			titles = decodePattern(titles, numbers.get(i));
		}
		int count = 0;
		int index = 0;
		boolean skip = false;
		while (count < titles.size()){
			temp.addAll(getSong("http://gdata.youtube.com/feeds/api/videos?v=2&q=" + URLEncoder.encode(titles.get(index), "UTF-8"), 1, 10, filter));
			skip = false;
			for (int i = 0; i < temp.size() && index < titles.size(); i++){
				if (Configure.getInstance().searchType.equals(SearchType.START_WITH)){
					if (temp.get(i).title.startsWith(titles.get(index))){
						songs.add(temp.get(i));
						index++;
						i = -1;
						skip = true;
					}
				} else {
					if (temp.get(i).title.equals(titles.get(index))){
						songs.add(temp.get(i));
						index++;
						i = -1;
						skip = true;
					}
				}
			}
			if (!skip) index++;
			if (index >= titles.size()) break;
			count++;
		}
		return songs;
	}
	
	private List<String> decodePattern(List<String> patterns, String number){
		List<String> values = new ArrayList<String>();
		int[] range = getRange(number);
		for (int i = 0; i < patterns.size(); i++){
			Matcher matcher = pattern.matcher(patterns.get(i));
			for (int j = range[0]; j <= range[1]; j++){
				values.add(matcher.replaceFirst(String.valueOf(j)));
			}
		}
		return values;
	}
	
	public List<Video> relativeOfVideo(String id, int page, String filter) throws UnsupportedEncodingException, IOException{
		return getSong("http://gdata.youtube.com/feeds/api/videos/" + id + "/related?v=2", page, MAX_RESULT, filter);
	}
	
	public List<Video> userVideos(String id, int page, String filter) throws UnsupportedEncodingException, IOException{
		return getSong("http://gdata.youtube.com/feeds/api/users/" + id + "/uploads?v=2", page, MAX_RESULT, filter);
	}
	
	public List<Video> getPlaylist(String id, int page, String filter) throws IOException{
		return getSong("http://gdata.youtube.com/feeds/api/playlists/" + id + "?v=2", page, MAX_RESULT, filter);
	}
	
	public List<Video> searchPlaylist(String value, int page, String filter) throws UnsupportedEncodingException, IOException{
		List<Video> songs = new ArrayList<Video>();
		URL url = new URL("http://gdata.youtube.com/feeds/api/playlists/snippets?alt=json&q=" + URLEncoder.encode(value, "UTF-8") + "&start-index=" + (((page - 1) * MAX_RESULT) + 1) + "&max-results=" + MAX_RESULT + "&v=2" + filter);
		BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
		String str;
		while ((str = in.readLine()) != null) {
			System.out.println(str);
		}
		in.close();
		return songs;
	}
	
	private String getValue(String line){
		int from = line.indexOf("\": \"") + 4;
		int to = line.lastIndexOf("\"");
		if (to == -1){
			System.out.println("ERROR IN  [GET VALUE] OF Youtube.class");
		}
		return line.substring(from, to == -1 ? line.length() : to).replace("\\", "");
	}

	public static String toDuration(long duration){
		long hour = (duration / 3600);
		long min = (duration % 3600) / 60;
		long sec = duration % 60;
		return (hour == 0 ? "" : numberToString(hour) + ":") +  numberToString(min) + ":" + numberToString(sec);
	}
	
	private static String numberToString(long number){
		String ret = String.valueOf(number);
		while (ret.length() < 2){
			ret = "0" + ret;
		}
		return ret;
	}
	
	private List<Video> getSong(String link, int page, int maxResult, String filter) throws IOException{
		List<Video> songs = new ArrayList<Video>();
		URL url = new URL(link + "&start-index=" + (((page - 1) * maxResult) + 1) + defaultFilter + maxResult + "&fields=" + fields + filter);
		BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
		String str = null;
		String title = "", description = "", duration = "0", id = "", viewCount = "", author = "", userId = "";
		boolean hd = false;
		while ((str = in.readLine()) != null){
			if (str.contains("\"name\"")){
				author = getValue(in.readLine());
				while ((str = in.readLine()) != null){
					if (str.contains("yt$userId")){
						userId = getValue(in.readLine());
						continue;
					}else if (str.contains("yt$hd")){
						hd = true;
						continue;
					}else if (str.contains("media$description")){
						description = getValue(in.readLine());
						continue;
					}else if (str.contains("media$title")){
						title = getValue(in.readLine());
						continue;
					}else if (str.contains("yt$duration")){
						duration = getValue(in.readLine());
						continue;
					}else if (str.contains("yt$videoid")){
						id = getValue(in.readLine());
						continue;
					}else if (str.contains("yt$statistics")){
						viewCount = getValue(in.readLine());
						Video song = new Video();
						song.username = author;
						song.userId = userId;
						song.duration = toDuration(new Long(duration));
						song.title = title;
						song.description = description;
						song.viewCount = formatter.format(new Long(viewCount));
						song.link = id;
						song.hd = hd;
						hd = false;
						songs.add(song);
						break;
					}
				}
			}
		}
		in.close();
		return songs;
	}
	
	public List<String> suggest(String line) throws IOException{
		List<String> suggets = new ArrayList<String>();
		URL url = new URL("http://clients1.google.com/complete/search?client=youtube&ds=yt&hl=vi&gl=vn&q=" + URLEncoder.encode(line, "UTF-8"));
		URLConnection connection = url.openConnection();
		BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(),"ISO-8859-1"));
		String str;
		while ((str = in.readLine()) != null) {
			int from =  str.indexOf("[[");
			if (from == -1) break;
			str = str.substring(from);
			while ((from = str.indexOf("[\"")) != -1){
				suggets.add(unescape(str.substring(from + 2, from = str.indexOf("\"", from + 2))));
				str = str.substring(from);
			}
		}
		in.close();
		return suggets;
	}
	
	public String unescape(String input){
		StringBuilder data = new StringBuilder(input);
		int index;
		while ((index = data.indexOf("\\u")) != -1) {
			data.replace(index, index+6, String.valueOf((char)Integer.parseInt(data.substring(index + 2, index + 6).toString(), 16)));
		}
		return data.toString();
	}
	
	public void SongsToM3UFile(List<Video> songs, String file) throws IOException{
		BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
		writer.write("#EXTM3U");
		writer.newLine();
		for (int i = 0; i < songs.size(); i++){
			Video song = songs.get(i);
			writer.write("#EXTINF:-1," + song.getTitle());
			writer.newLine();
			writer.write(song.getLink());
			writer.newLine();
		}
		writer.flush();
		writer.close();
	}
	
	public String toProperCase(String input) {
        Matcher m = p.matcher(input);  
        StringBuffer sb = new StringBuffer(input.length());  
        while (m.find()) {  
            m.appendReplacement(sb, m.group(1).toUpperCase());  
        }  
        m.appendTail(sb);  
        return sb.toString();  
	}
}
